/*
 * Diego da Rocha Borges
 * Otávio Zabaleta
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "symbol_table.h"

void init_symbol_table(symbol_table* table)
{
    int x = 0;
    for (x = 0; x < TABLE_SIZE; x++) {
        table->entries[x] = NULL;
    }
}

int hash_address(char *lexeme)
{
    int i = 0;
    int address = 1;
    for (i = 0; i < strlen(lexeme); i++) {
        address = ((address * lexeme[i]) % TABLE_SIZE) + 1;
    }
    return address;
}

/**
 * inserts the entry node into the table address.
 *
 * <p>
 * In case that the address already has an entry, then, the new element will be
 * added to the begining of the linked list of entries for that address.
 * </p>
 *
 * @param table the symbol table
 * @param entry_node the new entry node to be inserted into the symbol table.
 */
void insert_entry_node(symbol_table* table, table_entry_node* entry_node)
{
    int address = hash_address(entry_node->data->lexeme);

    if(table->entries[address] == NULL) {
        //printf("[adding] lexeme: %s, address: %d\n", entry_node->data->lexeme, address);
        table->entries[address] = entry_node;
    } else {
        //printf("[linking] lexeme: %s, address: %d\n", entry_node->data->lexeme, address);
        entry_node->next = table->entries[address];
        table->entries[address] = entry_node;
    }
}

table_entry_node* insert_entry(symbol_table* table, char* lexeme, int type)
{
    table_entry_node* entry_node = NULL;
    
    /*
     * if the lexeme already exists in the table, a pointer to the entry node is
     * returned.
     */
    entry_node = lookup(*table, lexeme, type);
    if (entry_node != NULL) {
        //printf("lexeme %s already exists!\n", lexeme);
        return entry_node;
    }
    /*
     * At this point, the lexeme was not found in the table, so a new entry node
     * is created to insert the lexeme into the table.
     */
    entry_node = create_entry_node(lexeme, type);
    insert_entry_node(table, entry_node);

    return entry_node;
}

table_entry_node* lookup(symbol_table table, char* lexeme, int type) {
    int address = hash_address(lexeme);
    table_entry_node* current_node = table.entries[address];

    if(current_node != NULL) {

        //printf("lexeme: %s, type: %d\n", lexeme, type);
        //printf("node lexeme: %s, type: %d\n", current_node->data->lexeme, current_node->data->type);
        while(current_node != NULL) {
            if ((strcmp(lexeme, current_node->data->lexeme) == 0) &&
                (type == current_node->data->type)) {

                return current_node;
            }
            current_node = current_node->next;
        }
        return current_node;

    } else {
        return NULL;
    }
}

table_entry_node* create_entry_node(char* lexeme, int type)
{
    table_entry* entry = malloc(sizeof(table_entry));
    char* lexemeCopy = malloc(strlen(lexeme) * sizeof(char));
    strcpy(lexemeCopy, lexeme);
    entry->lexeme = lexemeCopy;
    entry->type = type;
    //printf("[creating] lexeme %s, token %d\n", lexeme, token);

    table_entry_node* entry_node = malloc(sizeof(table_entry_node));
    entry_node->data = entry;
    entry_node->next = NULL;
    return entry_node;
}

void print_symbol_table(symbol_table table)
{
    int address = 1;
    int i;
  
    printf("####### Symbol Table #######\n");
    for (i = 0; i < TABLE_SIZE; i++) {
        table_entry_node* current_node = table.entries[i];
        while(current_node != NULL) {
            printf("[@%d]: type: %d, lexeme: %s, token: %d\n", i, current_node->data->type, current_node->data->lexeme);
            current_node = current_node->next;
        }
    }
    printf("############################\n");
}